#include "Problem3.hpp"

using namespace Ipopt;

Problem3::Problem3(Index dimension) :
	UnconstrainedProblem(dimension) {
	assert(dimension % 4 == 0);
}

bool Problem3::get_nlp_info(Index& n, Index& nnz_h_lag) {
	n = dimension;
	nnz_h_lag = 0;

	return true;
}

bool Problem3::get_starting_point(Index n, Number* x) {

	for (Index i = 0; i < dimension; i += 4) {
		x[i] = 3;
		x[i + 1] = -1;
		x[i + 2] = 1;
		x[i + 3] = 1;
	}

	return true;
}

bool Problem3::eval_f(Index n, const Number* x, bool new_x, Number& obj_value) {

	Number sum = 0, a, b, c, d;
	for (int j = 3; j < n; j += 4) {
		a = x[j - 3] + 10 * x[j - 2];
		b = x[j - 1] - x[j];
		c = x[j - 2] - 2 * x[j - 1];
		d = x[j - 3] - x[j];
		sum += a * a + 5 * b * b + c * c * c * c + 10 * d * d * d * d;
	}
	obj_value = sum;
	return true;
}

/** Method to return the gradient of the objective */
bool Problem3::eval_grad_f(Index n, const Number* x, bool new_x, Number* grad_f) {

	Number d;
	for (int j = 3; j < n; j += 4) {
		d = x[j - 3] - x[j];
		grad_f[j - 3] = 2 * x[j - 3] + 20 * x[j - 2] + 40 * d * d * d;
		grad_f[j] = 10 * x[j] - 10 * x[j - 1] - 40 * d * d * d;

		d = x[j - 2] - 2 * x[j - 1];
		grad_f[j - 2] = 20 * x[j - 3] + 200 * x[j - 2] + 4 * d * d * d;
		grad_f[j - 1] = 10 * x[j - 1] - 10 * x[j] - 8 * d * d * d;
	}

	return true;
}

void Problem3::newX(const Number* x) {
}

